package shared.file

import java.io._

trait IOAware {
  def execClose[A <: Closeable, B](c: A) (f: A => B):B = {
    try {
      return f(c)
    } finally {
      c.close
    }
  }
  
  def defaultBuffer = new Array[Byte](10240)
  
  def eachChunk[B](int: InputStream) (f: (Array[Byte], Int) =>B): Unit = {
    eachChunk(int, defaultBuffer)(f)
  }
  
  def eachChunk[B](in: InputStream, buffer: Array[Byte])(func: (Array[Byte], Int) => B):Unit = {
    if(buffer.size == 0) error("cannot use empty array as buffer")
    execClose(in) { _ => 
      var read = 0
      do {
        read = in.read(buffer)
        if(read >= 0) {
          func(buffer, read)
        }
      } while(read >= 0)
    }
  }
  
  class TransferSource(val in: InputStream) extends IOAware {
    private def doTransfer(out: OutputStream):Unit = {
      val buffer = new Array[byte](10240)
      var read = 0
      do {
        read = in.read(buffer)
        out.write(buffer, 0, read)
      } while(read >= 0)
    }
    
    def transferTo(out: OutputStream) = {
      execClose(out) { _ => 
        eachChunk(in, new Array[byte](10240)) { (b, max) => out.write(b, 0, max)}  
      }
    }
    
    def >>(out: OutputStream) = transferTo(out)
    
    def eachChunk[B] (f: (Array[Byte], Int)=>B):Unit = eachChunk(in, defaultBuffer) (f)
    
  }
  
  implicit def in2s (in: InputStream) = new TransferSource(in)
  
}

class TransferTask(val in: InputStream, val out: OutputStream, val max: Long) extends Runnable with IOAware {
  
  @volatile private var _speed = 0.0
  private var lastTime = 0L
  @volatile private var total = 0L
  def speed = _speed * 1000000.0
  
  @volatile private var done = false
  
  def percentage = total.asInstanceOf[Double] / max.asInstanceOf[Double]
  
  def finished = done
  
  def waitFor = synchronized {
    while (!done) {
      wait(1000)
    }
  }
  
  def run = {
    eachChunk(in){(buf, len) =>
      if(lastTime == 0L) {
        lastTime = System.nanoTime
      }
      out.write(buf, 0, len)
      val t = System.nanoTime - lastTime
      _speed = len.asInstanceOf[Double] / t.asInstanceOf[Double]
      lastTime = System.nanoTime
      total = total + len
    }
    synchronized {
      done = true
    }
  }
  

}
